#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>

#include <pcap.h>

#include <string>
#include "header.h"

std::string format_time(time_t tm);

int main(int argc, char * argv[])
{
	setuid(getuid());
	char errbuf[PCAP_ERRBUF_SIZE];

	if (argc != 3)
	{
		printf("usage: ./test device filter\n");
		return -1;
	}

	//char * dev_ptr = pcap_lookupdev(errbuf);
	char * dev_ptr = argv[1];
	char * filter_exp = argv[2];

	if (!dev_ptr)
	{
		printf("pcap_lookupdev fail: %s\n", errbuf);
		return -1;
	}


	bpf_u_int32 mask;		/* The netmask of our sniffing device */
	bpf_u_int32 net;		/* The IP of our sniffing device */
	in_addr addr;
	if (pcap_lookupnet(dev_ptr, &net, &mask, errbuf) == -1)
	{
		printf("pcap_lookupnet fail: %s\n", errbuf);
		return -1;
	}

	printf("device: %s\n", dev_ptr);
	addr.s_addr = net;
	printf("net ip: %s\n", inet_ntoa(addr));
	addr.s_addr = mask;
	printf("net mask: %s\n", inet_ntoa(addr));

	pcap_t * descr =  pcap_open_live(dev_ptr, 65535, 0, 1000, errbuf);
	if (!descr)
	{
		printf("pcap_open_live fail: %s\n", errbuf);
		return -1;
	}

	struct bpf_program fp;		/* The compiled filter */
	/* Compile and apply the filter */
	if (pcap_compile(descr, &fp, filter_exp, 0, net) == -1) 
	{
		printf("Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(descr));
		return -1;
	}

	if (pcap_setfilter(descr, &fp) == -1) 
	{
		printf("Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(descr));
		return -1;
	}


	printf("start grab on dev: %s\n", dev_ptr);

	pcap_pkthdr * hdr_ptr;
	const u_char * packet_ptr;
	int result = 0;
	char time_buffer[256] = {0};

	while ((result = pcap_next_ex(descr, &hdr_ptr, &packet_ptr)) >= 0)
	{
		if (result == 0)
		{
			//printf("pcap_next_ex fail packets are being read from a live capture, and the timeout expired\n");
			continue;
		}
	
		const struct sniff_ethernet *ethernet; /* The ethernet header */
		const struct sniff_ip *ip; /* The IP header */
		const struct sniff_tcp *tcp; /* The TCP header */
		const char *payload; /* Packet payload */

		u_int size_ip;
		u_int size_tcp;

		ethernet = (struct sniff_ethernet*)(packet_ptr);
		if (ethernet->ether_type != 8)
		{
			continue;
		}

		ip = (struct sniff_ip*)(packet_ptr + SIZE_ETHERNET);
		size_ip = IP_HL(ip)*4;
		if (size_ip < 20) 
		{
			printf("   * Invalid IP header length: %u bytes\n", size_ip);
			return -1;
		}
		if (ip->ip_tos != 0)
		{
			continue;
		}


		tcp = (struct sniff_tcp*)(packet_ptr + SIZE_ETHERNET + size_ip);
		size_tcp = TH_OFF(tcp)*4;
		if (size_tcp < 20) 
		{
			printf("   * Invalid TCP header length: %u bytes\n", size_tcp);
			return -1;
		}

		payload = (const char *)(packet_ptr + SIZE_ETHERNET + size_ip + size_tcp);

    	printf("[%s %ld] [caplen: %d] [len: %d] [src_ip: %s] [dst_ip: %s] [ether_type: %d] [tos: %d]\n"
    		, format_time(hdr_ptr->ts.tv_sec).c_str()
    		, hdr_ptr->ts.tv_usec
    		, hdr_ptr->caplen
    		, hdr_ptr->len
    		, inet_ntoa(ip->ip_src)
    		, inet_ntoa(ip->ip_dst)
    		, ethernet->ether_type
    		, ip->ip_tos);
	}

	return 0; 
}

std::string format_time(time_t t)
{
	char time_buffer[256] = {0};
	struct tm tmpTm;
	localtime_r(&t, &tmpTm);

	strftime(time_buffer, 256, "%Y-%m-%d %H-%M-%S", &tmpTm);
	return time_buffer;
}